package main

import (
	"bufio"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"github.com/go-co-op/gocron"
	"golang.org/x/crypto/salsa20"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"time"
)

type Rule struct {
	Alert      string            `yaml:"alert"`      // 必填，告警名称
	TimeRange  int               `yaml:"timeRange"`  // 必填，查询时间范围
	Index      string            `yaml:"index"`      // 必填，数据索引，支持模糊匹配
	Frequency  int               `yaml:"frequency"`  // 必填，探测频率，
	MatchField map[string]string `yaml:"matchField"` // 必填，匹配字段
	Trigger    string            `yaml:"trigger"`    // 必填，触发条件
	Annotation string            `yaml:"annotation"` // 必填，规则注释
}

// CronTask 定时任务创建管理
func CronTask() {
	s := gocron.NewScheduler(time.UTC)

	// 获取所有规则
	rules, err := ReadRuleFile()
	if err != nil {
		log.Fatalf("加载规则失败: %v", err)
		return
	}

	for _, rule := range rules {
		log.Debugf("发现监控项: %s", rule.Alert)

		// 将规则中的频率转换为 cron 表达式
		frequency := rule.Frequency
		var expr string
		if frequency == 1 {
			expr = "* * * * *"
		} else if frequency == 60 {
			expr = "0 * * * *"
		} else {
			expr = fmt.Sprintf("*/%d * * * *", frequency)
		}

		r := rule
		_, err := s.Cron(expr).Do(func() {
			log.Debugf("执行检索: %s", r.Alert)

			// 执行检索
			fieldMap, _ := EsSearch(r.MatchField, r.Index, r.TimeRange)
			// 告警触发判断
			Trigger(fieldMap, r.Trigger, r.Alert)
		})
		if err != nil {
			log.Fatalf("创建任务失败: %v", err)
		}
	}

	// 启动任务调度器
	s.StartAsync()
}

// ReadRuleFile 读取并解析所有规则文件
func ReadRuleFile() ([]Rule, error) {
	// 从配置文件中获取规则文件路径
	rulesDir, err := GetConfig("default", "RulesDir")
	if err != nil {
		return nil, fmt.Errorf("获取规则目录失败: %w", err)
	}

	// 使用 filepath.Glob 匹配规则文件
	files, err := filepath.Glob(filepath.Join(rulesDir, "*.yaml"))
	if err != nil {
		return nil, fmt.Errorf("匹配规则文件失败: %w", err)
	}

	if len(files) == 0 {
		log.Printf("未找到规则文件: %s", rulesDir)
		return nil, nil
	}

	var allRules []Rule
	for _, file := range files {
		rules, err := ReadSingleRuleFile(file)
		if err != nil {
			log.Printf("无法读取或解析文件 %s: %v", file, err)
			continue
		}
		allRules = append(allRules, rules...)
	}
	return allRules, nil
}

// ReadSingleRuleFile 读取并解析单个规则文件
func ReadSingleRuleFile(file string) ([]Rule, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败: %w", err)
	}
	defer func(f *os.File) {
		err := f.Close()
		if err != nil {
			log.Println(err)
		}
	}(f)

	var rules []Rule
	decoder := yaml.NewDecoder(bufio.NewReader(f))
	err = decoder.Decode(&rules)
	if err != nil && err.Error() != "EOF" {
		return nil, fmt.Errorf("解析文件 %s 失败: %w", file, err)
	}

	// 检查解析结果
	if len(rules) == 0 {
		log.Printf("文件 %s 中未找到规则", file)
	}
	return rules, nil
}

// CheckRequiredFields 检查必填字段是否存在，并验证字段名称是否符合规范
func CheckRequiredFields(rule Rule) {
	// 定义必填字段及其对应的 yaml 标签
	requiredFields := map[string]string{
		"Alert":      "alert",
		"TimeRange":  "timeRange",
		"Index":      "index",
		"Frequency":  "frequency",
		"MatchField": "matchField",
		"Trigger":    "trigger",
		"Annotation": "annotation",
	}

	// 获取 Rule 结构体的反射值和类型
	val := reflect.ValueOf(rule)
	typ := val.Type()

	for fieldName, yamlTag := range requiredFields {
		// 获取字段的反射值
		fieldValue := val.FieldByName(fieldName)
		if !fieldValue.IsValid() {
			log.Fatalf("规则缺少 %s 字段", yamlTag)
		}

		// 检查字段是否为零值
		if fieldValue.IsZero() {
			log.Fatalf("规则缺少或无效的 %s.%s 字段", rule.Alert, yamlTag)
		}

		// 获取字段的标签
		structField, ok := typ.FieldByName(fieldName)
		if !ok {
			log.Fatalf("字段 %s 不存在于 Rule 结构体中", fieldName)
		}

		// 获取字段的 yaml 标签
		yamlTagFromStruct := structField.Tag.Get("yaml")
		if yamlTagFromStruct != yamlTag {
			log.Fatalf("字段 %s 的 yaml 标签不正确，期望 %s，实际 %s", fieldName, yamlTag, yamlTagFromStruct)
		}
	}
}

// CheckYaml 检查规则名是否合法
func CheckYaml() {
	rules, err := ReadRuleFile()
	if err != nil {
		log.Fatalf("读取规则文件失败: %v", err)
	}

	var alertList []string

	for _, rule := range rules {
		alertList = append(alertList, rule.Alert)

		// 检查必填字段是否存在
		CheckRequiredFields(rule)

		// 检查 matchField 中的别名格式
		validAliasRegex := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)
		for alias := range rule.MatchField {
			if !validAliasRegex.MatchString(alias) {
				log.Fatalf("规则 %s 的 matchField 中别名 %s 格式不正确，只支持英文+下划线+数字的组合，且至少要有一个英文",
					rule.Alert, alias)
			}
		}

		// 检查 trigger 中使用的别名是否在 matchField 中定义
		triggerAliases := make(map[string]bool)
		for alias := range rule.MatchField {
			triggerAliases[alias] = true
		}

		// 解析 trigger 表达式中的变量
		parsedTrigger := rule.Trigger
		currentVariable := ""
		inVariable := false
		for _, char := range parsedTrigger {
			if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == '_' {
				currentVariable += string(char)
				inVariable = true
			} else if inVariable {
				if currentVariable != "" {
					if validAliasRegex.MatchString(currentVariable) {
						if !triggerAliases[currentVariable] {
							log.Fatalf("规则 %s 的 trigger 中使用了未定义的别名: %s", rule.Alert, currentVariable)
						}
					}
					currentVariable = ""
				}
				inVariable = false
			}
		}
		if inVariable && currentVariable != "" {
			if validAliasRegex.MatchString(currentVariable) {
				if !triggerAliases[currentVariable] {
					log.Fatalf("规则 %s 的 trigger 中使用了未定义的别名: %s", rule.Alert, currentVariable)
				}
			}
		}
	}

	// 使用字典模拟集合
	set := make(map[string]bool)
	for _, e := range alertList {
		if set[e] {
			log.Fatalf("发现重复的规则：" + e)
		} else {
			// 把元素添加到集合中
			set[e] = true
		}
	}
}

// SalsaCrypt 加密/解密函数
func SalsaCrypt(input string, isEncrypt bool) (string, error) {
	// 获取密钥
	key, err := GetConfig("settings", "key")
	if err != nil {
		return "", err
	}
	if len(key) != 32 {
		return "", fmt.Errorf("密钥长度错误，必须为32字节")
	}

	var keyBytes [32]byte
	copy(keyBytes[:], []byte(key))

	if isEncrypt {
		// 加密
		var nonce [24]byte
		if _, err := rand.Read(nonce[:]); err != nil {
			return "", err
		}

		ciphertext := make([]byte, len(input))
		salsa20.XORKeyStream(ciphertext, []byte(input), nonce[:], &keyBytes)

		// 拼接随机数和密文，并进行 Base64 编码
		encoded := base64.StdEncoding.EncodeToString(append(nonce[:], ciphertext...))
		return encoded, nil
	} else {
		// 解密
		data, err := base64.StdEncoding.DecodeString(input)
		if err != nil {
			return "", fmt.Errorf("base64 解码失败: %v", err)
		}

		if len(data) < 24 {
			return "", fmt.Errorf("解密数据太短")
		}

		// 提取随机数和密文
		var nonce [24]byte
		copy(nonce[:], data[:24])
		ciphertext := data[24:]

		plaintext := make([]byte, len(ciphertext))
		salsa20.XORKeyStream(plaintext, ciphertext, nonce[:], &keyBytes)

		return string(plaintext), nil
	}
}

// GetConfig 返回配置文件指定key的value
func GetConfig(sn string, key string) (string, error) {
	if config == nil {
		return "", fmt.Errorf("配置文件未加载")
	}
	section, err := config.GetSection(sn)
	if err != nil {
		return "", err
	}
	value := section.Key(key).String()
	if value == "" {
		return "", fmt.Errorf("key %s in section %s does not exist", key, sn)
	}
	return value, nil
}
